# coding=utf-8
import fitz
import pandas as pd
import re
import os


class Invoice_Ticket_Formating:
    def __init__(self, FilePath):
        self.Doc = fitz.open(FilePath)
        self.contextLocationInfo = ""
        self.HeaderDic = {}
        self.itemInfoCon = {}
        self.text_Con = {}

    def LoadPDF(self):
        """ 
        加载PDF文件并转化为DATAFRAME格式，进行表检索
        """
        OutputInfo = []
        pageCount = 0
        for page in self.Doc:
            pageCount += 1
            textList = page.get_text("blocks")
            for con in textList:
                t = ["x1", "y1", "x2", "y2", "context", "index", "isImage"]
                dic = dict(zip(t, con))
                # dic['pageNum'] = pageCount
                dic["y1"] = dic["y1"] + (pageCount - 1) * 1000
                OutputInfo.append(dic)
        self.contextLocationInfo = pd.DataFrame(OutputInfo)
        # contextLocationInfo.to_csv('tmp.csv')

    @staticmethod
    def ContextExit(anything):
        """ 
        判断是否获取到信息，如果没有则无返回
        """
        if anything is not None and anything != [] and anything != "":
            return anything
        else:
            return None

    def GetPdfHeader(self):
        """ 
        对PDF进行重新检索，并找到文件定位位置，获取信息
        """
        outputDT = {}
        page1 = self.Doc.load_page(0)
        searchList = [
            "Invoice Number\n",
            "Invoice Date\n",
            "Mode\n",
            "Terms of Delivery and payment\n",
        ]
        for search in searchList:
            l = self.contextLocationInfo[
                self.contextLocationInfo["context"] == search
            ].iloc[0]
            x1, y1, x2, y2 = l["x1"] - 5, l["y1"] - 5, l["x2"] + 50, l["y2"] + 20
            rect = (x1, y1, x2, y2)
            textList = page1.get_text("text", clip=rect)
            # print(textList.replace('\n',':')[:-1])
            tl = textList.split("\n")
            outputDT[tl[0]] = tl[1]
        self.HeaderDic = outputDT

    def GetBookingNo(self):
        self.contextLocationInfo["tag"] = self.contextLocationInfo.context.apply(
            lambda x: 1
            if re.findall(r"Carrier", x) != []
            and re.findall(r"Departure Date", x) != []
            and re.findall(r"Port of exit", x) != []
            else 0
        )

        location = self.contextLocationInfo[self.contextLocationInfo["tag"] == 1].iloc[
            0
        ]
        y1, y2 = location["y1"], location["y2"] + 20
        tmp = self.contextLocationInfo[
            (self.contextLocationInfo["y1"] > y1)
            & (self.contextLocationInfo["y1"] < y2)
        ]
        # tmp[ 180 > tmp.x1 > 190]
        self.HeaderDic["Carrier"] = self.ContextExit(
            tmp[(tmp.x1 > 10) & (tmp.x1 < 20)].context.iloc[0].strip()
        )
        self.HeaderDic["Departure_Port"] = self.ContextExit(
            tmp[(tmp.x1 > 300) & (tmp.x1 < 320)].context.iloc[0].strip()
        )
        t = self.ContextExit(
            tmp[(tmp.x1 > 180) & (tmp.x1 < 200)].context.iloc[0].strip().split("\n")
        )
        for _t in t:
            t1, t2, t3 = len(_t.split("/")), _t.count("/"), re.findall(r"\d", _t)
            if t1 == 3 and t2 == 2 and len(t3) == 8:
                self.HeaderDic["ETD"] = "{0[2]}-{0[0]}-{0[1]}".format(_t.split("/"))
            else:
                self.HeaderDic["BookingNo"] = _t

        self.contextLocationInfo["tag"] = self.contextLocationInfo.context.apply(
            lambda x: 1
            if re.findall(r"Container", x) != []
            and re.findall(r"Seal", x) != []
            and re.findall(r"Sz", x) != []
            else 0
        )
        location = self.contextLocationInfo[self.contextLocationInfo["tag"] == 1].iloc[
            0
        ]
        y1, y2 = location["y1"] - 2, location["y2"] + 2
        tmp = self.contextLocationInfo[
            (self.contextLocationInfo["y1"] > y1)
            & (self.contextLocationInfo["y1"] < y1 + 4)
        ].context.tolist()
        for _t in "\n".join(tmp).split("\n"):
            if _t != "" and ":" not in _t:
                # print(_t,re.findall(r'^[A-Z]{3,5}\s?-?\s?\w+\s?,\s?[A-Z]{2}$',_t ))
                if (
                    len(_t.split("/")) == 3
                    and _t.count("/") == 2
                    and len(re.findall(r"\d", _t)) == 8
                ):
                    self.HeaderDic["ETA"] = "{0[2]}-{0[0]}-{0[1]}".format(_t.split("/"))
                elif (
                    len(re.findall(r"^[0-9]{0,3}[A-Z]{1}", _t)) == 1
                    and len(re.findall(r"^[0-9]{0,3}[A-Z]{1}", _t)[0]) > 2
                ):
                    self.HeaderDic["ContanerSize"] = _t
                elif len(re.findall(r"^\d{4,10}", _t)) == 1:
                    self.HeaderDic["Seal_No"] = _t
                elif len(re.findall(r"^[A-Z]{2,7}\d{4,8}-?\d{0,8}", _t)) == 1:
                    self.HeaderDic["ContanerNo"] = _t
                elif (
                    len(re.findall(r"^[A-Z]{3,5}\s?-?\s?\w+\s?,\s?[A-Z]{2}$", _t)) == 1
                ):
                    self.HeaderDic["Arrival_Port"] = _t
        # print(self.HeaderDic)

    def ItemHaderFunc(self):
        """ 
        获取表中Item二级数据，对ItemCode和不同的PoNumer进行提取
        """
        # 获取目标数据
        self.contextLocationInfo["tag"] = self.contextLocationInfo.context.apply(
            lambda x: 1
            if re.findall(r"Pack Factor", x) != []
            and re.findall(r"Buyers reference", x) != []
            and re.findall(r"HS Number", x) != []
            else 0
        )
        l = self.contextLocationInfo[self.contextLocationInfo["tag"] == 1].to_dict(
            "record"
        )

        # 目标数据提取
        itemInfoCon = []
        for lst_ind in l:
            tmp = self.contextLocationInfo[
                (self.contextLocationInfo["y1"] > lst_ind["y1"] - 1)
                & (self.contextLocationInfo["y1"] < lst_ind["y1"] + 1)
            ][["y1", "context"]]
            context = tmp["context"].to_list()
            itemDic = {}
            l1 = context[0].split("\n")
            itemDic["Itemno"] = l1[0].strip()
            for tmp_Context in l1:
                if "Buyers reference:" in tmp_Context:
                    itemDic["Pono"] = tmp_Context.replace(
                        "Buyers reference:", ""
                    ).strip()
            l2 = context[1].split("\n")
            itemDic["TP"] = l2[0].strip().replace(",", "")
            itemDic["Quantity_TTL"] = l2[1].strip()
            itemDic["Units"] = l2[2].strip()
            itemDic["ystart"] = tmp.iloc[0]["y1"]
            itemInfoCon.append(itemDic)

        # 获取偏移位置和范围
        for ind, con in enumerate(itemInfoCon[:-1]):
            con.update({"yend": itemInfoCon[ind + 1]["ystart"]})
        itemInfoCon[-1].update({"yend": itemInfoCon[-1]["ystart"] + 80000})

        self.itemInfoCon = itemInfoCon

    def GetLotAndFormenating(self):
        """ 
        提取Lot中相关信息'lotno','Quantity','Uom','Coo'
        """
        self.contextLocationInfo["tag"] = self.contextLocationInfo.context.apply(
            lambda x: 1 if re.findall(r"^Lot:", x) != [] else 0
        )
        lotDF = self.contextLocationInfo[self.contextLocationInfo["tag"] == 1]

        text_Con = []
        for itemInfo in self.itemInfoCon:
            con = lotDF[(lotDF.y1 > itemInfo["ystart"]) & (lotDF.y1 < itemInfo["yend"])]
            # print(con)
            rePatten = r"^Lot:\s?([A-Za-z0-9]+).+?([,0-9]+)/(CA|EA|BX)\s+?Country of origin:\s?([A-Za-z_ ]+)"
            for text in con["context"].to_list():
                tmp = re.findall(rePatten, text)[0]
                # print(text, tmp)
                tmp = dict(zip(["lotno", "Quantity", "Uom", "Coo"], tmp))
                tmp["Quantity"] = tmp["Quantity"].replace(",", "")
                tmp.update(itemInfo)
                tmp.pop("yend")
                tmp.pop("ystart")
                tmp.update(self.HeaderDic)
                text_Con.append(tmp)
        return text_Con

    def CurrencyAndInvioce(self):
        patten = (
            r"TOTAL:\s" + self.HeaderDic["Terms of Delivery and payment"].strip()[:3]
        )
        self.contextLocationInfo["tag"] = self.contextLocationInfo.context.apply(
            lambda x: 1 if re.findall(patten, x) != [] else 0
        )
        tmp = self.contextLocationInfo[self.contextLocationInfo["tag"] == 1]
        tmp = tmp.context.values[0]
        # print(tmp)
        return {
            "Currency": tmp.strip().split("\n")[0],
            "Invoice Total": tmp.strip().split("\n")[-1].replace(",", ""),
        }

    # print(text_Con)
    def Run(self):
        self.LoadPDF()
        self.GetPdfHeader()
        self.GetBookingNo()
        self.ItemHaderFunc()

        text_Con = self.GetLotAndFormenating()
        dic = self.CurrencyAndInvioce()
        df = pd.DataFrame(text_Con)
        df["Terms"] = df["Terms of Delivery and payment"].apply(lambda x: x.strip()[:3])
        df["Currency"] = dic["Currency"]
        df["Invoice Total"] = dic["Invoice Total"]
        # print(self.contextLocationInfo)
        # print(df)
        return df


def filepath_getter(path: str):
    """
    + path: str 输入指定目录地址
    + return :: dict {文件路径:文件大小}
    """
    output_paths: dict = {}
    # 遍历获取的文件名
    for f in os.listdir(path) if not os.path.isfile(path) else [path]:
        p: str = path + "/" + f
        n: int = 1 + len(p.split("/")) - len(path.split("/"))

        if os.path.isdir(p):
            # print(".{} {}".format("--" * n, p))
            output_paths.update(filepath_getter(p))  # 递归下

        elif os.path.isfile(p) and p.lower().endswith(".pdf"):
            s = os.path.getsize(p)
            # 调用函数获取文件大小和相应单位
            # filesize, units = file_size_formating(s)
            # print("|{} {: >7.2f} {: >2} >> {}".format(
            #       "--" * n, filesize, units, f))
            output_paths[p] = s
    return output_paths


if __name__ == "__main__":

    with open('path.txt','r',encoding='utf8') as f:
        filePath = f.read()
    # filePath = "Soruce"
    filePathList = filepath_getter(filePath)

    _df = pd.DataFrame()
    for k, v in filePathList.items():
        print(k, v)
        df = Invoice_Ticket_Formating(FilePath=k).Run()
        df["Invoice Date"] = df["Invoice Date"].apply(
            lambda x: "{0[2]}-{0[0]}-{0[1]}".format(x.split("/"))
        )
        df["TRANS_MODE"] = df["Mode"]
        _df = pd.concat([_df, df])
        # df2 = Invoice_Ticket_Formating.contextLocationInfo.to_csv('t{}.csv'.format(k))
    dfOut = _df[
        [
            "Invoice Number",
            "Invoice Date",
            "TRANS_MODE",
            "Terms",
            # "Carrier",
            "Departure_Port",
            "ETD",
            "Arrival_Port",
            "ETA",
            "BookingNo",
            "ContanerNo",
            "ContanerSize",
            "Seal_No",
            "Itemno",
            "Quantity",
            "Uom",
            "lotno",
            "Pono",
            "Coo",
            "TP",
            "Currency",
            "Invoice Total",
        ]
    ].copy()
    dfOut["Invoice Date"] = pd.to_datetime(dfOut["Invoice Date"], format="%Y/%m/%d") 
    dfOut["ETD"] = pd.to_datetime(dfOut["ETD"], format="%Y/%m/%d") 
    dfOut["ETA"] = pd.to_datetime(dfOut["ETA"], format="%Y/%m/%d") 
    cNameFormating = []
    for i in dfOut.columns:
        cNameFormating.append(i.upper().replace(" ", "_"))
    # print(cNameFormating)
    dfOut.columns = cNameFormating
    print(dfOut)
    dfOut.info()
    dfOut.to_excel(r"{}\\inbound_invoice.xlsx".format(filePath), index=False)
    print("End")
